package com.xquant.trade.trade.service.impl;

import cn.hutool.core.util.StrUtil;
import com.xquant.common.security.utils.SecurityUtils;
import com.xquant.trade.systerm.mapper.InstrumentMapper;
import com.xquant.trade.trade.entity.Balance;
import com.xquant.trade.trade.entity.BalanceChange;
import com.xquant.trade.trade.entity.Trade;
import com.xquant.trade.trade.entity.TradeStatus;
import com.xquant.trade.trade.mapper.SettleMapper;
import com.xquant.trade.trade.mapper.TradeMapper;
import com.xquant.trade.trade.mapper.TradePkgMapper;
import com.xquant.trade.trade.service.SettleService;
import com.xquant.trade.trade.service.TradeService;
import com.xquant.trade.trade.service.impl.other.TradeServiceImpl4SimpleTrade;
import com.xquant.trade.utils.CommResponse;
import com.xquant.trade.utils.DateUtils;
import org.apache.commons.collections.ListUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.*;

import static java.math.BigDecimal.ROUND_HALF_DOWN;

/**
 * 交易抽象类 基础方法
 */
public abstract class TradeServiceAbstract implements TradeService {
    private static Logger logger = LoggerFactory.getLogger(TradeServiceImpl4SimpleTrade.class);
    @Autowired
    protected TradeMapper tradeMapper;
    @Autowired
    protected TradePkgMapper tradePkgMapper;
    @Autowired
    protected SettleMapper settleMapper;
    @Autowired
    protected SettleService settleService;
    @Autowired
    private InstrumentMapper instrumentMapper;
    private org.slf4j.Logger log = LoggerFactory.getLogger(SettleServiceImpl.class);


    @Autowired
    protected TradeServiceFactory factory;
    private static Map<String, List> cacheMap = new HashMap();

    /*
     * 交易前 基础信息处理 默认值
     * */
    @Override
    public void prepareTrade(Trade trade) {
        trade.setOcFlag("O");
        trade.setDimLs("L");
        if ("OtcOptionSell".equals(trade.getTradeType())) {
            trade.setPayDirection("-1");
        } else if ("OtcOptionBuy".equals(trade.getTradeType())) {
            trade.setPayDirection("1");
        }
        trade.setTradeTime(DateUtils.getCurr_YYYY_MM_DD_HH_MM_SS());
        trade.setUserId(SecurityUtils.getUserId().toString());
//        if (StringUtils.isEmpty(trade.getSetDate())) {
//            trade.setSetDate(trade.getTradeDate());
//        }
        if (trade.getTradeStatus() == null) {
            trade.setTradeStatus(TradeStatus.DEAL);
        }
    }

    /**
     * 检查交易信息
     */
    @Override
    public CommResponse checkTrade(Trade trade) {
        checkTradeDate(trade);
        return CommResponse.success();
    }

    /**
     * 检查交易信息
     */
    @Override
    public CommResponse checkTradeDate(Trade trade) {
        if (!trade.getTradeDate().equals(settleService.getBlcDate())) {
            return new CommResponse(false, "只支持业务日期交易的操作！");
        }
        return CommResponse.success();
    }

    /**
     * 新增交易
     */
    @Override
    public CommResponse insertTrade(Trade trade) {
        CommResponse commResponse = CommResponse.success();
        prepareTrade(trade);
        tradeMapper.insertTrade(trade);
        return commResponse;
    }

    /**
     * 成交
     */
    @Override
    public CommResponse confirmTrade(Trade trade) {
        CommResponse commResponse = CommResponse.success();

        return CommResponse.success();
    }

    /**
     * 更新交易
     */
    @Override
    public CommResponse updateTrade(Trade trade) {
        prepareTrade(trade);
        // 更新主交易
        int updateRows = tradeMapper.updateTrade(trade);

        return CommResponse.success(updateRows);
    }

    /**
     * 交易撤单前校验
     */
    @Override
    public CommResponse checkRevertTrade(Trade trade) {

        return new CommResponse(true);
    }

    /**
     * 交易撤单 作废 9
     */
    @Override
    public void revertTrade(Trade trade) {

    }

    /**
     * 删除交易
     */
    @Override
    public void deleteTrade(Trade trade) {

    }


    /**
     * 交易 风险检查
     */
    @Override
    public CommResponse checkBeforeApproval(Trade trade, Map<String, Map> map) {
        Map<String, Object> ret = new WeakHashMap<>();
        ret.put("warn", ListUtils.EMPTY_LIST);
        ret.put("forbidden", ListUtils.EMPTY_LIST);
        return CommResponse.success(ret);
    }

    /**
     * -----------------------------------------------交易导入方法---begin------------------------------------------------------------
     */
    public CommResponse importTrades(HttpServletRequest request, MultipartFile cmf, String tradeType, Map validMap) {
        // 获得上传文件的文件名

        return CommResponse.success();
    }

    @Override
    public Trade tranferTrade(Trade trade) {
        return trade;
    }


    /**
     * -----------------------------------------------交易导入方法---end------------------------------------------------------------
     */
    public List<BalanceChange> getBalanceChange(Trade trade) {
        List<BalanceChange> lst = new ArrayList<>();
        BalanceChange c1 = createSelfSecuChange(trade);
        BalanceChange c2 = createSelfCashChange(trade);
        BalanceChange c3 = createCpSecuChange(trade);
        BalanceChange c4 = createCpCashChange(trade);
        if (c1 != null) {
            lst.add(c1);
        }
        if (c2 != null) {
            lst.add(c2);
        }
        if (c4 != null) {
            lst.add(c4);
        }
        if (c3 != null) {
            lst.add(c3);
        }
        return lst;
    }

    protected BalanceChange createSelfSecuChange(Trade trade) {
        if (trade.getiCode() == null) {
            return null;
        }
        BalanceChange c = new BalanceChange();
        c.setChangeDate(trade.getTradeDate());
        c.setTradeId(trade.getTradeId());
        c.setiCode(trade.getiCode());
        c.setaType(trade.getaType());
        c.setmType(trade.getmType());
        c.setOcFlag(trade.getOcFlag());
        c.setVolume(trade.getVolume());
        c.setFee(trade.getFee());
        c.setAmount(trade.getAmount());
        c.setMargin(trade.getMarginCost());
        c.setBonus(trade.getBonus());
        c.setAi(trade.getAi());
        c.setDueVolume(trade.getDueVolume());
        c.setDueBonus(trade.getDueBonus());
        c.setDueAi(trade.getDueAi());
        c.setCurrency(trade.getCurrency());
        c.setDimAccId(String.valueOf(trade.getAccId()));
        c.setDimLs(trade.getDimLs());
        c.setDimExtAccId(trade.getDimExtAccId());
        c.setDimPCode(trade.getpCode());
        c.setDimSource(trade.getDimSource());
        c.setDimIr(trade.getDimIr());
        c.setDimGuid(trade.getDimGuid());
        c.setDimExpireDate(trade.getDimExpireDate());
        return c;
    }

    protected BalanceChange createSelfCashChange(Trade trade) {
        BigDecimal volume = BigDecimal.ZERO.add(trade.getAmount()).add(trade.getFee()).add(trade.getAi());
        if (StrUtil.equals("Margin", trade.getTradeType())) {
            //volume = BigDecimal.ZERO.add(trade.getAmount()).add(trade.getFee()).add(trade.getMargin()).add(trade.getAi());
            volume = trade.getMarginCost();
        }


        if (BigDecimal.ZERO.compareTo(volume) == 0) {
            return null;
        }
        BalanceChange c = new BalanceChange();
        c.setChangeDate(trade.getSetDate());
        c.setTradeId(trade.getTradeId());
        c.setiCode(trade.getiCode());
        c.setaType("CASH");
        c.setmType("NONE");
        c.setOcFlag("O");
        c.setDimLs("L");
        c.setVolume(volume);
        c.setAmount(volume.multiply(new BigDecimal(-1)));
        if (!StrUtil.equals("Margin", trade.getTradeType())) {
            c.setVolume(BigDecimal.ZERO);
            c.setAmount(BigDecimal.ZERO);
        }
        c.setCurrency(trade.getCurrency());
        c.setDimAccId(String.valueOf(trade.getAccId()));
        return c;
    }

    protected BalanceChange createCpSecuChange(Trade trade) {
        if (trade.getAccIdCp() == null || trade.getiCode() == null) {
            return null;
        }
        BalanceChange c = new BalanceChange();
        c.setChangeDate(trade.getTradeDate());
        c.setTradeId(trade.getTradeId());
        c.setiCode(trade.getiCode());
        c.setaType(trade.getaType());
        c.setmType(trade.getmType());
        c.setOcFlag(trade.getOcFlag());
        c.setVolume(trade.getVolume().multiply(new BigDecimal(-1)));
        c.setFee(trade.getFee().multiply(new BigDecimal(-1)));
        c.setAmount(trade.getAmount().multiply(new BigDecimal(-1)));
        c.setMargin(trade.getMarginCost().multiply(new BigDecimal(-1)));
        c.setBonus(trade.getBonus().multiply(new BigDecimal(-1)));
        c.setAi(trade.getAi().multiply(new BigDecimal(-1)));
        c.setDueVolume(trade.getDueVolume().multiply(new BigDecimal(-1)));
        c.setDueBonus(trade.getDueBonus().multiply(new BigDecimal(-1)));
        c.setDueAi(trade.getDueAi().multiply(new BigDecimal(-1)));
        c.setCurrency(trade.getCurrency());
        c.setDimAccId(String.valueOf(trade.getAccIdCp()));
        c.setDimLs(trade.getDimLs());
        c.setDimExtAccId(trade.getDimExtAccId());
        c.setDimPCode(trade.getpCode());
        c.setDimSource(trade.getDimSource());
        c.setDimIr(trade.getDimIr());
        c.setDimGuid(trade.getDimGuid());
        c.setDimExpireDate(trade.getDimExpireDate());
        return c;
    }

    protected BalanceChange createCpCashChange(Trade trade) {
        if (trade.getAccIdCp() == null) {
            return null;
        }
        BigDecimal volume = BigDecimal.ZERO.subtract(trade.getAmount()).subtract(trade.getFee()).subtract(trade.getAi());
        if (StrUtil.equals("Margin", trade.getTradeType())) {
            //volume = BigDecimal.ZERO.subtract(trade.getAmount()).subtract(trade.getFee()).subtract(trade.getMargin()).subtract(trade.getAi());
            volume = BigDecimal.ZERO.subtract(trade.getMarginCost());
        }

        if (volume.equals(BigDecimal.ZERO)) {
            return null;
        }
        BalanceChange c = new BalanceChange();
        c.setChangeDate(trade.getSetDate());
        c.setTradeId(trade.getTradeId());
        c.setiCode(trade.getCurrency());
        c.setaType("CASH");
        c.setmType("NONE");
        c.setOcFlag("O");
        c.setDimLs("L");
        c.setVolume(volume);
        c.setAmount(volume.multiply(new BigDecimal(-1)));
        if (!StrUtil.equals("Margin", trade.getTradeType())) {
            c.setVolume(BigDecimal.ZERO);
            c.setAmount(BigDecimal.ZERO);
        }
        c.setCurrency(trade.getCurrency());
        c.setDimAccId(String.valueOf(trade.getAccIdCp()));
        return c;
    }

    protected List<BalanceChange> mergeBalanceChange(List<BalanceChange> changes1, List<BalanceChange> changes2) {
        List<BalanceChange> ret = new ArrayList<>();
        if (changes1 != null) {
            ret.addAll(changes1);
        }
        if (changes2 == null) {
            return ret;
        }
        changes2.forEach(change2 -> {
            for (int i = 0; i < changes1.size(); i++) {
                BalanceChange change1 = changes1.get(i);
                if (match(change1, change2)) {
                    return;
                }
            }
            ret.add(change2);
        });
        // 投资组合相关的标的都需要重跑
        ret.forEach(item -> {
            item.setDimSource("*");
            item.setDimIr("*");
            item.setDimGuid("*");
            item.setDimExpireDate("*");
        });
        return ret;
    }

    protected boolean match(BalanceChange change1, BalanceChange change2) {
        if (change1 == null || change2 == null) {
            return false;
        }
        return org.apache.commons.lang3.StringUtils.equals(change1.getChangeDate(), change2.getChangeDate()) &&
                org.apache.commons.lang3.StringUtils.equals(change1.getiCode(), change2.getiCode()) &&
                org.apache.commons.lang3.StringUtils.equals(change1.getaType(), change2.getaType()) &&
                org.apache.commons.lang3.StringUtils.equals(change1.getmType(), change2.getmType()) &&
                org.apache.commons.lang3.StringUtils.equals(change1.getDimAccId(), change2.getDimAccId()) &&
                org.apache.commons.lang3.StringUtils.equals(change1.getDimLs(), change2.getDimLs()) &&
                org.apache.commons.lang3.StringUtils.equals(change1.getDimExtAccId(), change2.getDimExtAccId()) &&
                StringUtils.equals(change1.getDimPCode(), change2.getDimPCode());
    }
    /***-------------------------------------trade_pkg-------begin--------------------------------------------------*/
    /**
     * -- 回滚至前一天的持仓
     */
    public CommResponse tradeRollbackBalance(BalanceChange balanceChange) {
        //1.1 删除历史持仓
        try {
            tradePkgMapper.deleteBalanceHis(balanceChange);
            tradePkgMapper.deleteBalance(balanceChange);
            if (balanceChange.getChangeDate() != null) {
                tradePkgMapper.insertBalance(balanceChange);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            return CommResponse.success(ex.getMessage());
        }
        logger.info("回滚至前一天的持仓");
        return CommResponse.success("回滚历史持仓！");
    }

    /**
     * -- 处理交易
     */
    public CommResponse tradeProcessTrade(BalanceChange balanceChange) {
        logger.info("处理交易");
        CommResponse commResponse = new CommResponse(true);
        List<BalanceChange> list = tradePkgMapper.queryBalanceChange(balanceChange);
        for (int i = 0; i < list.size(); i++) {
            BalanceChange bc = list.get(i);
//            System.out.println("bc:" + bc.toString());
            commResponse = this.tradeProcessBalanceChange(bc);
            if (!commResponse.isSuccess()) {
                break;
            }
        }
        return commResponse;
    }

    /**
     * -- 处理一次变动
     */
    public CommResponse tradeProcessBalanceChange(BalanceChange balanceChange) {
        logger.info("处理一次变动");

        BigDecimal volumeLeft = new BigDecimal(0); //剩余数量
        BigDecimal amountLeft = new BigDecimal(0); //剩余金额
        BigDecimal feeLeft = new BigDecimal(0); //剩余费用
        BigDecimal marginLeft = new BigDecimal(0); //剩余保证金
        BigDecimal dVolume = new BigDecimal(0); //变动数量
        BigDecimal dAmount = new BigDecimal(0); //变动金额
        BigDecimal dAmountCost = new BigDecimal(0); //变动成本
        BigDecimal dPrftTrd = new BigDecimal(0); //变动交易损益
        BigDecimal dPrftFee = new BigDecimal(0); //变动费用损益
        BigDecimal dMargin = new BigDecimal(0); //保证金变动
        try {
            //开仓
            if (StrUtil.equals(balanceChange.getOcFlag(), "O")) {
                List<Balance> list = tradePkgMapper.queryBalance(balanceChange);
                if (list.size() > 0) {
                    tradePkgMapper.updateBalance(balanceChange);
                } else {
                    tradePkgMapper.insertBalanceByChange(balanceChange);
                }
                return CommResponse.success("开仓成功!");
            }
            //平仓
            BigDecimal pVolume = balanceChange.getVolume();
            BigDecimal pAmount = balanceChange.getAmount();
            BigDecimal pFee = balanceChange.getFee();
            BigDecimal pMargin = balanceChange.getMargin();
            String pLs = balanceChange.getDimLs();
            List<Balance> listBalances = tradePkgMapper.queryBalanceByVolumn(balanceChange);
            volumeLeft = pVolume;
            amountLeft = pAmount;
            feeLeft = pFee;
            marginLeft = pMargin;
            for (int i = 0; i < listBalances.size(); i++) {
                Balance balance = listBalances.get(i);
                //保证金
                if (balance.getMarginCost().multiply(marginLeft).doubleValue() > 0) {
                    if (Math.abs(balance.getMarginCost().doubleValue()) > Math.abs(pMargin.doubleValue())) {
                        dMargin = marginLeft.multiply(new BigDecimal(-1));
                    } else {
                        dMargin = balance.getMarginCost();
                    }
                    marginLeft = marginLeft.add(dMargin);
                } else {
                    dMargin = new BigDecimal(0);
                }
                //--数量
                if (balance.getVolume().add(volumeLeft).multiply(volumeLeft).doubleValue() > 0) {
                    dVolume = balance.getVolume().multiply(new BigDecimal(-1));
                } else {
                    dVolume = volumeLeft;
                }
                //--成本变化(负) = 成本(正)*持仓变动数量(负)/持仓数量(正)
                if (balance.getVolume().compareTo(new BigDecimal(0)) == 0) {
                    dAmountCost = new BigDecimal(0);
                } else {
                    dAmountCost = balance.getAmountCost().multiply(dVolume).divide(balance.getVolume(), 2, ROUND_HALF_DOWN).setScale(2);
                }
                // --变动金额(正) = 剩余金额(正)*持仓变动数量(负)/总的变动量(负)
                if (pVolume.compareTo(new BigDecimal(0)) == 0) {
                    dAmount = amountLeft;
                } else {
                    dAmount = amountLeft.multiply(dVolume).divide(pVolume, 2, ROUND_HALF_DOWN).setScale(2);
                }
                //--交易损益 = 变动金额(正)+成本变化(负)
                if (StrUtil.equals(pLs, "S")) {
                    dPrftTrd = dAmount.add(dAmountCost).multiply(new BigDecimal(-1));
                } else {
                    dPrftTrd = dAmount.add(dAmountCost);
                }
                //--费用损益(负) = 剩余费用(负)*持仓变动数量(负)/总的变动量(负)
                if (pVolume.compareTo(new BigDecimal(0)) == 0) {
                    dPrftFee = feeLeft;
                } else {
                    dPrftFee = feeLeft.multiply(dVolume).divide(pVolume, 2, ROUND_HALF_DOWN).setScale(2);
                }
                feeLeft = feeLeft.subtract(dPrftFee);
                volumeLeft = volumeLeft.subtract(dVolume);

                balance.setVolume(dVolume);
                balance.setAmountCost(dAmountCost);
                balance.setPrftTrd(dPrftTrd);
                balance.setPrftFee(dPrftFee);
                balance.setMarginCost(dMargin);
                balance.setPrftAi(balanceChange.getAi());
                balance.setDueAi(balanceChange.getDueAi());
                balance.setDueBonus(balanceChange.getDueBonus());
                balance.setDueVolume(balanceChange.getDueVolume());
                tradePkgMapper.updateBalanceByChange(balance);
            }
            if (volumeLeft.compareTo(new BigDecimal(0)) != 0) {
                return CommResponse.failure("标的平仓数量不足，处理失败! <br/> " + " 标的代码：" + balanceChange.getiCode());
            }
            if (marginLeft.compareTo(new BigDecimal(0)) != 0) {
                return CommResponse.failure("平仓保证金不足，处理失败! <br/> " + "标的代码：" + balanceChange.getiCode());
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            return CommResponse.failure("处理一次变动失败" + ex.getMessage());
        }
        return CommResponse.success("处理一次变动成功");
    }

    /**
     * -- 备份持仓
     */
    public CommResponse tradeBackupBalance(BalanceChange balanceChange) {
        try {
            //--更新历史
            tradePkgMapper.updateBalanceHis(balanceChange);
            //--插入历史
            tradePkgMapper.insertBalanceHis(balanceChange);
            // --当前表数据日期+1
            tradePkgMapper.updateBalanceBlcDate(balanceChange);
        } catch (Exception ex) {
            ex.printStackTrace();
            return CommResponse.failure("备份持仓失败:" + ex.getMessage());
        }
        return CommResponse.success("备份成功");
    }

    //删除其他交易 更
    public CommResponse tradeDeleteAllTrade(Trade trade) {
        try {
            tradePkgMapper.deleteBalanceChangeAll(trade);
            tradePkgMapper.deleteTradeAll(trade);
            tradePkgMapper.deleteBalanceAll(trade);
            tradePkgMapper.deleteBalanceHisAll(trade);
        } catch (Exception ex) {
            ex.printStackTrace();
            return CommResponse.failure("删除交易失败:" + ex.getMessage());
        }
        return CommResponse.success("删除交易成功");
    }

    public CommResponse tradeDeleteAllBalance(Trade trade) {
        try {
            tradePkgMapper.deleteBalanceChangeAll(trade);
            tradePkgMapper.deleteBalanceAll(trade);
            tradePkgMapper.deleteBalanceHisAll(trade);
        } catch (Exception ex) {
            ex.printStackTrace();
            return CommResponse.failure("删除持仓失败:" + ex.getMessage());
        }
        return CommResponse.success("删除持仓成功");
    }

    /**
     * -----------------------------------------------trade_pkg---end---------------------------------------------------------------
     */


}
